//
//  CubicBezierFunction.swift
//  BeepNow
//
//  Created by mikecrm-rt on 2019/11/29.
//  Copyright © 2019 RongTao. All rights reserved.
//

import UIKit
protocol BNTimeFunction {
    func value(forTime:Double) -> CGFloat
}
class CubicBezierFunction: NSObject,BNTimeFunction {
    var controlP1:CGPoint!
    var controlP2:CGPoint!
    var startPoint = CGPoint.zero
    var endPoint = CGPoint.init(x:1,y:1)
    convenience init(p1:CGPoint,p2:CGPoint,c1:CGPoint,c2:CGPoint) {
        self.init()
        startPoint = p1
        endPoint = p2
        controlP1 = c1
        controlP2 = c2
    }
    convenience init(c1:CGPoint,c2:CGPoint) {
        self.init()
        self.controlP1 = c1
        self.controlP2 = c2
    }
    convenience init(controlPoint1:CGPoint,controlPoint2:CGPoint) {
        self.init()
        self.controlP1 = controlPoint1
        self.controlP2 = controlPoint2
    }
    func getPoint(_ t:CGFloat)-> CGPoint {
        guard t >= 0 else {
            return startPoint
        }
        guard t <= 1 else {
            return endPoint
        }
        let temp = 1-t
        let c1x = controlP1.x
        let c1y = controlP1.y
        let c2x = controlP2.x
        let c2y = controlP2.y
        // 三次贝塞尔曲线点公式
        let x = startPoint.x * pow(temp, 3) + 3*c1x*pow(temp, 2)*t + 3*c2x*temp*pow(t, 2) + endPoint.x*pow(t, 3)
        let y = startPoint.y * pow(temp, 3) + 3*c1y*pow(temp, 2)*t + 3*c2y*temp*pow(t, 2) + endPoint.y*pow(t, 3)
        return CGPoint.init(x:x,y:y)
    }
    func getYForX(_ x:CGFloat)-> CGFloat { // 根据x，反求t，再求y
        if x >= 1 {
            return self.getPoint(1).y
        }
        if x <= 0 {
            return self.getPoint(0).y
        }
        // 将三次贝塞尔曲线公式展开为 a·t^3 + b·t^2 + c·t + d = 0
        let a =  -startPoint.x + 3*controlP1.x - 3*controlP2.x + endPoint.x
        let b = 3*startPoint.x - 6*controlP1.x + 3*controlP2.x
        let c = -3*startPoint.x + 3*controlP1.x
        let d = startPoint.x - x
        
        let p = c/a - b*b/(3*a*a)
        let q = d/a + 2*b*b*b/(27*a*a*a) - b*c/(3*a*a)
        var t:CGFloat
         if q*q/4+p*p*p/27 < 0 {
//            print("n为虚数",q*q/4+p*p*p/27,n)
            let close_t = self.getTForX(t: x, targetX: x,0)
            t = close_t
         } else {
            let m = -q/2
            let n = pow(q*q/4+p*p*p/27, 1.0/2)
            let signM:CGFloat = m+n >= 0 ? 1 : -1 // 帮助负数开立方保留符号
            let signN:CGFloat = m-n >= 0 ? 1 : -1
            t = signM * pow(abs(m+n), 1.0/3) + signN * pow(abs(m-n), 1.0/3) - b/(3*a) // 根据公式，代入x，反求t
//            print(p,q,m,n, t)
        }
        let temp = 1-t
        let c1y = controlP1.y
        let c2y = controlP2.y
        // 三次贝塞尔曲线点公式
        let y = startPoint.y * pow(temp, 3) + 3*c1y*pow(temp, 2)*t + 3*c2y*temp*pow(t, 2) + endPoint.y*pow(t, 3)
        return y
    }
    func value(forTime: Double) -> CGFloat {
        return self.getYForX(CGFloat(forTime))
    }
    func getTForX(t:CGFloat, targetX:CGFloat,_ i:Int)->CGFloat { // 针对单调的三次贝塞尔曲线
        let tempP = self.getPoint(t)
        let tempX = tempP.x
        let sub = targetX - tempX
        if abs(sub) > 0.001 {
            let nextT = t + sub/2 
//            print("targetX,tempX,sub,t,nextX,i\n",targetX,tempX,sub,t,nextT,i,"\n")
            if nextT <= 0 {
                return 0
            }
            return self.getTForX(t: nextT, targetX: targetX,i+1)
        } else {
            return t
        }
        
    }
    
}
extension CGPoint {
    init(_ x:CGFloat,_ y:CGFloat) {
        self.init(x:x,y:y)
    }
}
